package za.org.meraka.cair.ontorepair;

import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;

import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class HSGenerator<T>{
	private Set<Set<T>> f, hittingSets;
	private Set<Node<T>> currLevel,currTree;	
	private UtilityClass<T> u;
	private Iterator<Set<T>> fIterator;		
	private Node<T> root;
	private Object[] levelArr;	
	
	public HSGenerator(Set<Set<T>> hs){		
		u = new UtilityClass<T>();		
		f = new HashSet<Set<T>>(hs);
		currLevel = currTree = new HashSet<Node<T>>();
		hittingSets = new HashSet<Set<T>>();
	}	
		
	/**********************Access F**********************/
	private Node<T> getInter(Set<T> path, Node<T> parent){				
		for (Set<T> tmp: f){
			if (u.intersection(tmp, path).isEmpty())
				return new Node<T>(tmp, path, parent);
		}		
		Node<T> t = new Node<T>(new HashSet<T>(), path, parent);
		t.setLeaf(1);
		return t;
	}
	/****************************************************/
	
	/*********Reset all data**********/
	public void setF(Set<Set<T>> newF){		
		f = new HashSet<Set<T>>(newF);
		currLevel = currTree = new HashSet<Node<T>>();
		hittingSets = new HashSet<Set<T>>();
	}
	/*********************************/
	
	/********Check if HS-Tree is complete*******/
	private boolean finished(Set<Node<T>> level){					
		for (Node<T> tmp: level){		
			if (tmp.leaf != 1)
				return false;
		}						
		return true;
	}			
	/*******************************************/
	
	/****Tree pruning: 3(i) closing****/
	private boolean threeI(Set<T> path){			
		for (Node<T> tmp : currTree){			
			if (tmp.leaf == 1){
				if (path.containsAll(tmp.getPath())){
					return true;
				}
			}
		}		
		return false;
	}
	/**********************************/
	
	/****Tree pruning: 3(ii) closing****/
	private boolean threeII(Set<T> path){		
		for (Node<T> tmp: currTree){					
			if (path.equals(tmp.getPath())){
				return true;
			}			
		}		
		return false;
	}
	/***********************************/
	
	/********************Node Reuse***********************/
	private Node<T> getReused(Set<T> path, Node<T> parent){
		for (Node<T> tmp: currTree){
			if (tmp.leaf == 0)
				if (u.intersection(tmp.getData(), path).isEmpty())			
					return new Node<T>(tmp.getData(), path, parent);						
		}				
		return null;
	}
	/*****************************************************/
		
	private Node<T> processNode(Set<T> path, Node<T> parent){
		/**Node Reuse*/
		Node<T> reused = getReused(path, parent);
		if (reused != null){			
			return reused;
		}
		else{
			Node<T> res;
			/**Tree pruning 3(i)*/
			if (threeI(path)){	
				res = new Node<T>(new HashSet<T>(), new HashSet<T>(), parent);
				res.setLeaf(2);
				return res;
			}	
			/**Tree pruning 3(ii)*/
			if (threeII(path)){
				res = new Node<T>(new HashSet<T>(), new HashSet<T>(), parent);
				res.setLeaf(2);
				return res;
			}	
			/**Normal*/			
			res = getInter(path, parent);									
			return res;
		}
	}
				
	private void genTree(Set<Node<T>> level){
		/******************************Compute HS-Tree****************************/
		try{
		if (!finished(level)){		
			levelArr = new Object[level.size()];currLevel = new HashSet<Node<T>>();
			Node<T> tempNode;Set<T> data;T[] dataArr;int l = 0;
								
			for (Node<T> tmp: level){
				levelArr[l] = tmp;l++;
			}
									
			for (int i = 0; i < levelArr.length;i++){
				tempNode = (Node<T>)levelArr[i];				
				data = tempNode.getData();
				dataArr = (T[])data.toArray((T[])new Object[data.size()]);
				Node<T> nextNode;
				Set<T> path;
				if (tempNode.leaf == 0){
					for (int j = 0; j < dataArr.length;j++){						
						path = tempNode.getPrev(dataArr[j]);					
						nextNode = processNode(path, tempNode);
																									
						currTree.add(nextNode);
						currLevel.add(nextNode);																																																														
					}						
				}
			}
			genTree(currLevel);			
		}
		/***********************************************************************/
					
		/*********Choose HittingSets***********/
		for (Node<T> tmp : currTree){
			if (tmp.leaf == 1)
				hittingSets.add(tmp.getPath());
		}
		/**************************************/
		}
		catch (NullPointerException e){JOptionPane.showMessageDialog(new JPanel(),"Reasoner invocation error","This reasoner is not supported for this task.",JOptionPane.WARNING_MESSAGE);}
	}
	
	private void chooseRoot(Set<Set<T>> set){
		fIterator = f.iterator();
		if (fIterator.hasNext())
			root = new Node<T>(fIterator.next(), new HashSet<T>(), null);		
	}
		
	public Set<Set<T>> getHittingSets(){	
		chooseRoot(f);
		currTree.add(root);				
		Set<Node<T>> bSet = new HashSet<Node<T>>();
		bSet.add(root);
		genTree(bSet);
		return hittingSets;
	}	
}
